package com.demo.lambda;

import org.junit.Test;

import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * lambda 表达式练习
 * http://t.csdn.cn/kLRMZ
 * @author gy
 * @date 2022/5/6
 */
public class LambdaStudy {
    public static void main(String[] args) {
       // lambda 重要特征
        // 1、可选类型声明：不需要声明参数类型，编译器可以统一识别参数类型
        // 2、可选的参数圆括号：一个参数无需定义，
        //                  但多个参数需要定义
        // 3、可选的大括号：如果主体包含了一个语句，就不需要使用大括号
        //                  但多个语句，需要使用大括号
        // 4、可选的返回关键字：如果主体只有一个表达式，返回值编译器会自动返回
        //                  但主体有多个 表达式，则需要指定

    }

    /**
     * 利用lambda表达式对列表进行迭代
     */
    @Test
    public void m1(){
        List<String> list1 = Arrays.asList("java", "php", "c++", "c", "py", "c#");
//        list.forEach(n -> System.out.println(n));
        // java8 的另一个新特性，方法的引用，不过这种没有拌饭传递参数，只能 用 ::调用不需要参数的方法
        list1.forEach(System.out::println);
    }

    /**
     * 使用lambda表达式替代匿名内部类
     * () -> {}替代了匿名内部类
     */
    @Test
    public void m2(){
        // java8 之前
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("实现了 Runnable 接口的 run方法");
            }
        }).start();

        // java8之后 () -> {主体内容}
        new Thread(() -> {System.out.println("java 8");
            System.out.println("测试2");}).start();


    }


    /**
     * 使用lambda表达式和函数式接口Predicate
     * java8新增了一个包，叫做 java.util.function，接口里面包含了很多类，用来支持函数式编程，
     * 其中有一个接口就是 Predicate,用Predicate可以用来过滤，下面我们展示一下过滤集合的相关数据
     */
    @Test
    public void m3(){

        List<String> list1 = Arrays.asList("java", "php", "c++", "c", "py", "c#");
        Predicate<String> p1 = (n) -> n.startsWith("c");
        Predicate<String> p2 = (n) -> n.length() == 3;
        list1.stream().filter(p1).forEach(n -> System.out.println(n));

        /*
        直接在filter中写过滤条件或者传入一个Predicate都行，有人或许说既然可以直接写过滤条件，
        用Predicate是不是有点多余了，不不，继续看，你就知道为什么要用Predicate了  见 m4
         */
        list1.stream().filter( n -> n.startsWith("c")).forEach(n -> System.out.println(n));
    }

    /**
     * Predicate之多个过滤条件
     */
    @Test
    public void m4(){
        List<String> list1 = Arrays.asList("java", "php", "c++", "c", "py", "c#");
        Predicate<String> p1 = (n) -> n.startsWith("c");
        Predicate<String> p2 = (n) -> n.length() == 3;
        // and()将满足条件的结果查询出来
        list1.stream().filter(p1.and(p2)).forEach(System.out::println);
        System.out.println("===========================");
        // or()将满足其中任意一个条件的结果查询出来
        list1.stream().filter(p1.or(p2)).forEach(System.out::println);
    }

    /**
     * lambda表达式之map
     * map()统一对元素进行操作
     */
    @Test
    public void m5(){
        List<Double> list2 = Arrays.asList(100.0, 200.0, 300.0, 400.0, 500.0);
        list2.stream().map(n -> n * 0.2).forEach(System.out::println);
    }

    /**
     * lambda表达式之reduce()
     * reduce() 函数可以将所有值合并成一个。Map和Reduce操作是函数式编程的核心操作，
     * 因为其功能，reduce 又被称为折叠操作。
     * 另外，reduce 并不是一个新的操作，你有可能已经在使用它。
     * SQL中类似 sum()、avg() 或者 count() 的聚集函数，实际上就是 reduce 操作，因为它们接收多个值并返回一个值。
     * 流API定义的 reduce() 函数可以接受lambda表达式，并对所有值进行合并。
     * IntStream这样的类有类似 average()、count()、sum() 的内建方法来做 reduce 操作，
     * 也有mapToLong()、mapToDouble() 方法来做转换。这并不会限制你，你可以用内建方法，也可以自己定义
     */
    @Test
    public void m6(){
        List<Double> list2 = Arrays.asList(100.0, 200.0, 300.0);
        //  reduce 函数有些不太理解
        Double aDouble = list2.stream().map(n -> n * 0.2).reduce((sum, n) -> sum + n).get();
        System.out.println(aDouble);
    }

    /**
     * lambda表达式之collect()
     * 1、将操作之后的结果转换为集合
     * 2、将操作之后的结果进行连接
     */
    @Test
    public void m7(){
        List<Double> list2 = Arrays.asList(100.0, 200.0, 300.0, 400.0, 500.0);
        System.out.println("1、将操作之后的结果转换为集合");
        List<Double> result = list2.stream().filter(n -> n >= 200).collect(Collectors.toList());
        System.out.println(result);

        System.out.println("2、将操作之后的结果进行连接");
        List<String> list1 = Arrays.asList("java", "php", "c++", "c", "py", "c#");
        String result1 = list1.stream().map(n -> n.toUpperCase()).collect(Collectors.joining(","));
        System.out.println(result1);
    }

    /**
     * lambda表达式之distinct()去重
     */
    @Test
    public void m8(){
        List<String> list1 = Arrays.asList("java", "php", "c++", "c", "py", "c#", "c++", "c++");
        List<String> res = list1.stream().filter(n -> n.startsWith("c")).distinct().collect(Collectors.toList());
        System.out.println(res);
    }

    /**
     * 计算集合元素的最大值，最小值等
     * IntStream、LongStream 和 DoubleStream 等流的类中，有个非常有用的方法叫做 summaryStatistics() 。
     * 可以返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s，描述流中元素的各种摘要数据。
     * 在本例中，我们用这个方法来计算列表的最大值和最小值。它也有 getSum() 和 getAverage() 方法来获得列表的所有元素的总和及平均值
     */
    @Test
    public void m9(){
        List<Double> list2 = Arrays.asList(100.0, 200.0, 300.0, 400.0, 500.0);
        DoubleSummaryStatistics res = list2.stream().mapToDouble(n -> n * 2).summaryStatistics();
        System.out.println(res.getAverage());
        System.out.println(res.getMax());
    }

    /**
     * 注：lambda表达式有个限制，那就是只能引用 final 或 final 局部变量，
     *  这就是说不能在lambda内部修改定义在域外的变量。
     */
    @Test
    public void m10(){
        List<Double> list2 = Arrays.asList(100.0, 200.0, 300.0, 400.0, 500.0);
        int a = 2;
        // 不能在lambda内部修改定义在域外的变量。
//        list2.forEach(n -> a++);
    }

}

